package com.ruoyi.wetest.manager;

import com.alibaba.fastjson2.JSONObject;
import com.ggalmazor.ltdownsampling.LTThreeBuckets;
import com.ruoyi.wetest.config.WeTestConfig;
import com.ruoyi.wetest.domain.*;
import com.ruoyi.wetest.mapper.WetestStatLogMapper;
import com.ruoyi.wetest.mapper.WetestStressStatisticsMapper;
import com.ruoyi.wetest.mapper.WetestStressTransMapper;
import com.ruoyi.wetest.service.IWetestProjectsService;
import com.ruoyi.wetest.service.IWetestStressRecordService;
import com.ruoyi.wetest.service.IWetestStressTransMetricService;
import com.ruoyi.wetest.stats.DateSeriesPoint;
import com.ruoyi.wetest.stats.MessageMetric;
import com.ruoyi.wetest.utils.JsonHelper;
import com.ruoyi.wetest.utils.L1CacheKey;
import com.ruoyi.wetest.utils.L1CacheManager;
import com.ruoyi.wetest.utils.WeTestConst;
import com.ruoyi.wetest.vo.WeTestReportSummarizationVo;
import com.ruoyi.wetest.vo.WeTestStatInfoVo;
import com.ruoyi.wetest.vo.WeTestStressInfoVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.*;

import static com.ruoyi.wetest.utils.WeTestConst.ALL_CODE_NAME;

/**
 * @author Allen
 * @description 压测报告管理器
 * @Date 2024/5/10 19:40
 */
@Service
public class WetestReportManager {

    @Autowired
    private WeTestConfig weTestConfig;

    @Autowired
    private WetestManager wetestManager;

    @Autowired
    private IWetestStressRecordService iWetestStressRecordService;
    @Autowired
    private WetestStressStatisticsMapper wetestStressStatisticsMapper;
    @Autowired
    private WetestStressTransMapper wetestStressTransMapper;
    @Autowired
    private IWetestStressTransMetricService iWetestStressTransMetricService;

    @Autowired
    private IWetestProjectsService wetestProjectsService;

    @Autowired
    private WetestStatLogMapper wetestStatLogMapper;

    @Autowired
    private ResourceLoader resourceLoader;

    @Autowired
    private L1CacheManager l1CacheManager;

    public Map<String, Double> tpsRuleMap = new HashMap<>();

    @PostConstruct
    public void init() {
        JSONObject o = JsonHelper.jsonFileLoad(resourceLoader, "StressModuleConfig.json");
        for (String s : o.keySet()) {
            JSONObject o1 = o.getJSONObject(s);
            String name = o1.getString("name");
            Double tps = o1.getDouble("tps");
            tpsRuleMap.put(name, tps);
        }
    }


    public WeTestReportSummarizationVo getReportSummarization(String testId) {
        String infoKey = L1CacheKey.getStressReSumKey(testId);
        WeTestReportSummarizationVo wrs = l1CacheManager.get(infoKey);
        if (wrs == null) {
            wrs = new WeTestReportSummarizationVo();
            WetestStressRecord wr = iWetestStressRecordService.selectWetestStressRecordByTestId(testId);
            WeTestStressInfoVo ws = wetestManager.getWeTestStressInfoVoNullable(testId);
            if (wr == null) {
                if (ws == null) return wrs;
                return wrs;
            }
            WetestProjects projects = wetestProjectsService.selectWetestProjectsByProjectId(wr.getProjectId());
            if (projects == null) {
                return wrs;
            }

            Collection<WetestStressTransMetric> metricList = null;
            //判断是否正处于压测状态
            if (ws != null) {
                wr= wetestManager.addOrUpdateRecord(ws);
                MessageMetric messageMetric = ws.getMessageMetric();
                if (messageMetric != null) {
                    Map<String, WetestStressTransMetric> snapshotList = messageMetric.getSnapshotList();
                    metricList = snapshotList.values();
                    messageMetric.saveStatLog0();
                }
            } else {
                WetestStressTransMetric wetestStressTransMetric = new WetestStressTransMetric();
                wetestStressTransMetric.setTestId(testId);
                metricList = iWetestStressTransMetricService.selectWetestStressTransMetricList(wetestStressTransMetric);
            }

            assert metricList != null;
            Optional<WetestStressTransMetric> first = metricList.stream().filter(e -> Objects.equals(e.getTransName(), ALL_CODE_NAME)).findFirst();
            if (first.isPresent()) {
                WetestStressTransMetric metric = first.get();
                wrs.setTransSucRate(metric.getSuccRate().doubleValue());
                wrs.setRtAvgTime(metric.getAvgCost().doubleValue());
                wrs.setTransErrorNum(metric.getFailNum());
            }
            for (WetestStressTransMetric metric : metricList) {
                Double tps = tpsRuleMap.get(metric.getTransName());
                if (tps != null) {
                    metric.setExpectTps(tps);
                }
            }
            wrs.setTestId(testId);
            wrs.setProjectId(wr.getProjectId());
            wrs.setProjectName(projects.getProdectName());
            wrs.setStartTime(wr.getStartTime());
            wrs.setEndTime(wr.getEndTime());
            wrs.setTotalTrans(wr.getTransNum());
            wrs.setTpsAvgTimes(wr.getAllTps());
            wrs.setOpcu(wr.getOnlinePcu());
            wrs.setTpcu(wr.getTotalPcu());
            wrs.setOnlineData(conversionData(testId, WeTestConst.STAT_ONLINE, true));
            wrs.setRtRateData(conversionData(testId, WeTestConst.STAT_RT, false));
            wrs.setTxRateData(conversionData(testId, WeTestConst.STAT_TX, false));
            wrs.setTpsData(conversionData(testId, WeTestConst.STAT_TPS, false));
            wrs.setTransMetricList(metricList);
            wrs.setStatus(projects.getStatus());

            if (ws == null){
                l1CacheManager.set(infoKey, wrs);
            }
        }
        return wrs;
    }

    private List<Object[]> conversionData(String testId, String type, boolean inr) {
        WetestStatLog wetestStatLog = new WetestStatLog();
        wetestStatLog.setType(type);
        wetestStatLog.setTestId(testId);
        long i = 0;
        List<WetestStatLog> wetestStatLogs = wetestStatLogMapper.selectWetestStatLogListByGroup(wetestStatLog);
        List<DateSeriesPoint> in = new ArrayList<>();
        for (WetestStatLog log : wetestStatLogs) {
            long v = log.getValue();
            if (inr) {
                i += v;
                v = i;
            }
            in.add(new DateSeriesPoint(log.getTimestamps().intValue(), v));
        }
        return lttbHandle(in);
    }

    /**
     * 数据降噪
     *
     * @param in
     * @return
     */
    private List<Object[]> lttbHandle(List<DateSeriesPoint> in) {
        List<Object[]> ls = new ArrayList<>();
        //LTTB 降采样算法
        if (in.size() > 500) {
            int numberOfBuckets = 250; //最大数量
            in = LTThreeBuckets.sorted(in, numberOfBuckets);
        }
        for (DateSeriesPoint point : in) {
            ls.add(new Object[]{(long) point.getDate() * 1000, point.getY()});
        }
        return ls;
    }

    public List<Object[]> getStatListByType(String testId, String trans) {
        return conversionData(testId, trans, false);
    }

    public List<String> selectWetestStatLogTypeList(String testId) {
        WetestStatLog wetestStatLog = new WetestStatLog();
        wetestStatLog.setSubType(WeTestConst.STAT_TRANS_SUB_TYPE);
        wetestStatLog.setTestId(testId);
        return wetestStatLogMapper.selectWetestStatLogTypeList(wetestStatLog);
    }

    public WetestStressTransMetric getWetestStressTransMetric(String testId, String trans) {
        WetestStressTransMetric wetestStressTransMetric = new WetestStressTransMetric();
        wetestStressTransMetric.setTestId(testId);
        wetestStressTransMetric.setTransName(trans);
        List<WetestStressTransMetric> metrics = iWetestStressTransMetricService.selectWetestStressTransMetricList(wetestStressTransMetric);
        Optional<WetestStressTransMetric> first = metrics.stream().findFirst();
        return first.orElse(wetestStressTransMetric);
    }

    public WeTestStatInfoVo getStatInfo(String testId, String trans) {
        String infoKey = L1CacheKey.getStressTransStatInfoKey(testId, trans);
        WeTestStatInfoVo statInfoVo = l1CacheManager.get(infoKey);
        if (statInfoVo == null) {
            List<Object[]> list = getStatListByType(testId, trans);
            statInfoVo = new WeTestStatInfoVo();
            statInfoVo.setStatData(list);
            //WetestStressTransMetric transMetric = getWetestStressTransMetric(testId, trans);
            //statInfoVo.setMetric(transMetric);
            getStatCostInfo(testId, trans, statInfoVo);
            l1CacheManager.set(infoKey, statInfoVo);
        }
        return statInfoVo;
    }

    /**
     * 根据事务类型获取事务曲线
     *
     * @param testId
     * @param trans
     * @param wsi
     */
    private void getStatCostInfo(String testId, String trans, WeTestStatInfoVo wsi) {
        WetestStatLog wetestStatLog = new WetestStatLog();
        wetestStatLog.setType(trans + "_c");
        wetestStatLog.setTestId(testId);
        List<WetestStatLog> wetestStatLogs = wetestStatLogMapper.selectWetestStatLogListByType(wetestStatLog);
        List<DateSeriesPoint> maxDataIn = new ArrayList<>();
        List<DateSeriesPoint> minDataIn = new ArrayList<>();
        List<DateSeriesPoint> avgDataIn = new ArrayList<>();
        List<DateSeriesPoint> meanDataIn = new ArrayList<>();
        List<DateSeriesPoint> p90DataIn = new ArrayList<>();

        for (WetestStatLog log : wetestStatLogs) {
            int l = log.getTimestamps().intValue();
            String remark = log.getRemark();
            if (remark == null || remark.isEmpty()) continue;
            String[] split = remark.split("\\|");
            maxDataIn.add(new DateSeriesPoint(l, Double.parseDouble(split[0])));
            minDataIn.add(new DateSeriesPoint(l, Double.parseDouble(split[1])));
            avgDataIn.add(new DateSeriesPoint(l, Double.parseDouble(split[2])));
            meanDataIn.add(new DateSeriesPoint(l, Double.parseDouble(split[3])));
            p90DataIn.add(new DateSeriesPoint(l, Double.parseDouble(split[4])));
        }
        wsi.setMaxData(lttbHandle(maxDataIn));
        wsi.setMinData(lttbHandle(minDataIn));
        wsi.setAvgData(lttbHandle(avgDataIn));
        wsi.setMeanData(lttbHandle(meanDataIn));
        wsi.setP90Data(lttbHandle(p90DataIn));
    }


}
